3. 도커 개요1

2.1. 도커와 Build, Ship, Run

도커의 핵심 작업 흐름

Build, Ship, Run 개념:

도커 작업 흐름:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Build - 이미지 작성]
    │
    ├─ Dockerfile 작성
    ├─ 컨텍스트 준비
    └─ 이미지 빌드
    ↓

[Ship - 이미지 배포]
    │
    ├─ 레지스트리에 Push
    ├─ 이미지 공유
    └─ 다른 환경에서 Pull
    ↓

[Run - 컨테이너 실행]
    │
    ├─ 이미지로 컨테이너 생성
    ├─ 격리된 실행 환경 구성
    └─ 애플리케이션 실행

핵심:
→ Build: 한 번 작성
→ Ship: 어디든 배포
→ Run: 동일하게 실행
용어 정리

  • Build: 애플리케이션과 실행 환경을 컨테이너 이미지로 패키징하는 과정
  • Ship: 빌드된 이미지를 레지스트리에 업로드하고 다른 환경으로 배포하는 과정
  • Run: 이미지로부터 컨테이너를 생성하고 실행하는 과정
  • 컨테이너 이미지 (Container Image): 애플리케이션과 그 실행에 필요한 모든 것을 포함한 읽기 전용 패키지

컨테이너 이미지란?

컨테이너 이미지는 컨테이너 형태로 실행하고 싶은 애플리케이션의 구성 요소를 모두 포함한 패키지

컨테이너 이미지 구성:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[컨테이너 이미지]
    │
    ├─ 애플리케이션 바이너리
    │  └─ 실행 파일, 스크립트
    │
    ├─ 의존 파일
    │  └─ 라이브러리, 설정 파일
    │
    ├─ 실행 환경 설정
    │  └─ 환경 변수, 시작 명령어
    │
    └─ 루트 파일시스템
       └─ /bin, /lib, /usr 등

특징:
→ 읽기 전용 템플릿
→ 레이어 구조
→ 재사용 가능

2.1.1. Build: 컨테이너 이미지 작성

컨테이너 이미지 빌드 개념

이미지 빌드 = 컨테이너의 재료 준비

이미지 빌드 프로세스:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[입력]
    │
    ├─ Dockerfile
    │  └─ 컨테이너 작성 절차를 기록한 파일
    │
    └─ 컨텍스트 (Context)
       └─ 빌드에 사용할 파일들
          ├─ 애플리케이션 소스 코드
          ├─ 설정 파일
          └─ 스크립트
    ↓

[도커 빌드 엔진]
    │
    ├─ Dockerfile 명령어 실행
    ├─ 컨텍스트 파일 복사
    └─ 레이어별 이미지 생성
    ↓

[출력]
    │
    └─ 컨테이너 이미지
       └─ 모든 구성 요소가 통합된 패키지
용어 정리

  • Dockerfile: 컨테이너 이미지를 빌드하기 위한 명령어들을 담은 텍스트 파일
  • 컨텍스트 (Context): 빌드 시 Docker 데몬에 전송되는 파일 집합. 소스 코드, 설정 파일 등 포함
  • 빌드 엔진: Dockerfile을 해석하고 이미지를 생성하는 Docker 내부 컴포넌트
  • 레이어 (Layer): 이미지를 구성하는 파일시스템의 변경 단위. 각 명령어가 새 레이어 생성

실습: Hello World 이미지 빌드

1. 프로젝트 구조 준비:

프로젝트 디렉토리 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

myimage/
├─ hello.sh         (실행할 스크립트)
└─ Dockerfile       (빌드 명령 파일)

2. 파일 생성 (PowerShell):

# 실습 폴더를 생성하고, 이동하여 진행하는 것을 권장

# 디렉토리 생성
mkdir myimage

# hello.sh 스크립트 작성
$hello = "#!/bin/bash`nset -eu`necho `"Hello, World!`"`nexec sleep infinity`n"
[System.IO.File]::WriteAllText("$PWD\myimage\hello.sh", $hello)

# Dockerfile 작성
@"
FROM ubuntu:22.04
COPY ./hello.sh /hello.sh
RUN chmod +x /hello.sh
ENTRYPOINT ["/hello.sh"]
"@ | Set-Content myimage\Dockerfile -NoNewline

3. Dockerfile 구조 이해:

용어 정리:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[베이스 이미지 (Base Image)]
    │
    └─ 새 이미지의 기반이 되는 이미지
       FROM 명령어로 지정

[컨텍스트 (Context)]
    │
    └─ 빌드 시 Docker 데몬에 전송되는 파일 집합
       docker build 명령의 마지막 인자로 지정하는 경로

[chmod]
    │
    └─ Linux 파일 권한 변경 명령어
       +x는 실행 권한(execute) 부여

[ENTRYPOINT]
    │
    └─ 컨테이너 시작 시 항상 실행되는 명령
       CMD와 달리 덮어쓰기 어려움
Dockerfile 명령어 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

FROM ubuntu:22.04
└─ 베이스 이미지 지정
   └─ Ubuntu 22.04를 기반으로 시작

COPY ./hello.sh /hello.sh
└─ 컨텍스트에서 파일 복사
   └─ 로컬의 hello.sh를 컨테이너의 /hello.sh로 복사

RUN chmod +x /hello.sh
└─ 빌드 시 명령어 실행
   └─ hello.sh에 실행 권한 부여

ENTRYPOINT ["/hello.sh"]
└─ 컨테이너 시작 시 실행할 명령어
   └─ 컨테이너 실행 시 hello.sh 자동 실행
용어 정리

  • FROM: 베이스 이미지를 지정하는 Dockerfile 명령어. 모든 Dockerfile의 첫 명령어
  • COPY: 컨텍스트의 파일을 이미지로 복사하는 명령어
  • RUN: 빌드 시점에 명령어를 실행하여 새 레이어 생성. 패키지 설치 등에 사용
  • ENTRYPOINT: 컨테이너 시작 시 항상 실행되는 명령어. CMD와 달리 덮어쓰기 어려움
  • 베이스 이미지 (Base Image): 새 이미지의 기반이 되는 이미지. FROM으로 지정
  • chmod +x: Linux에서 파일에 실행 권한을 부여하는 명령어

4. 이미지 빌드 실행:

docker build -t myimage:v1 ./myimage

빌드 명령어 구조:

docker build 명령어 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

docker build -t myimage:v1 ./myimage
    │        │     │    │      │
    │        │     │    │      └─ 빌드 컨텍스트 경로
    │        │     │    │
    │        │     │    └─ 태그 (버전)
    │        │     │
    │        │     └─ 이미지 이름 (리포지터리)
    │        │
    │        └─ 태그 옵션
    │
    └─ 빌드 명령어

결과:
→ 이미지 이름: myimage
→ 태그: v1
→ 전체 이름: myimage:v1

빌드 과정 상세:

도커 빌드 실행 흐름:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[1단계] 컨텍스트 전송
    ↓
./myimage 디렉토리 내용을
도커 데몬으로 전송

[2단계] Dockerfile 파싱
    ↓
FROM ubuntu:22.04
├─ ubuntu:22.04 이미지 다운로드
└─ 베이스 레이어 생성

[3단계] 명령어 실행
    ↓
COPY ./hello.sh /hello.sh
├─ 새 레이어 생성
└─ hello.sh 파일 추가

RUN chmod +x /hello.sh
├─ 새 레이어 생성
└─ 실행 권한 변경

ENTRYPOINT ["/hello.sh"]
└─ 메타데이터에 시작 명령어 저장

[4단계] 이미지 생성
    ↓
모든 레이어를 합쳐
최종 이미지 생성

[5단계] 태깅
    ↓
myimage:v1 태그 부여

이미지 레이어 구조

도커 이미지의 레이어 시스템:

이미지 레이어 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[myimage:v1]
    │
    ├─ 레이어 4: ENTRYPOINT 메타데이터
    │             (읽기 전용)
    │
    ├─ 레이어 3: chmod +x /hello.sh
    │             (읽기 전용)
    │
    ├─ 레이어 2: hello.sh 파일 추가
    │             (읽기 전용)
    │
    └─ 레이어 1: ubuntu:22.04 베이스
                  (읽기 전용)

장점:
✓ 효율적 저장: 공통 레이어 재사용
✓ 빠른 빌드: 변경된 레이어만 재생성
✓ 캐시 활용: 이전 빌드 결과 재사용

레이어 캐싱 예시:

캐시 활용 메커니즘:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[첫 빌드]
FROM ubuntu:22.04        → 다운로드
COPY ./hello.sh /hello.sh → 복사
RUN chmod +x /hello.sh    → 실행
시간: 30초

[두 번째 빌드 - 변경 없음]
FROM ubuntu:22.04        → 캐시 사용
COPY ./hello.sh /hello.sh → 캐시 사용
RUN chmod +x /hello.sh    → 캐시 사용
시간: 1초

[hello.sh 수정 후 빌드]
FROM ubuntu:22.04        → 캐시 사용
COPY ./hello.sh /hello.sh → 재실행 (변경됨)
RUN chmod +x /hello.sh    → 재실행 (이전 단계 변경)
시간: 5초

최적화 팁:
→ 자주 변경되는 파일은 Dockerfile 하단에 배치
→ 의존성 설치는 상단에 배치
용어 정리

  • 레이어 캐싱 (Layer Caching): 이전 빌드의 레이어를 재사용하여 빌드 속도를 높이는 기법
  • 태그 (Tag): 이미지의 버전을 식별하는 레이블. 예: myimage:v1에서 v1이 태그
  • 리포지터리 (Repository): 동일한 이름의 이미지들을 모아둔 저장소. 태그로 버전 구분
  • -t 옵션: docker build에서 이미지 이름과 태그를 지정하는 옵션


2.1.2. Run: 컨테이너 실행

컨테이너 실행 기본

이미지에서 컨테이너 생성:

컨테이너 실행 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[이미지: myimage:v1]
    │ (읽기 전용 템플릿)
    ↓
[docker run 명령어]
    ↓
[컨테이너 생성]
    │
    ├─ 쓰기 가능 레이어 추가
    ├─ 네트워크 설정
    ├─ 격리된 환경 구성
    └─ ENTRYPOINT 실행
    ↓
[실행 중인 컨테이너]
    │
    └─ PID 1: /hello.sh
       └─ "Hello, World!" 출력
       └─ sleep infinity 실행

컨테이너 실행 명령어:

docker run --name mycontainer myimage:v1

명령어 구조:

docker run 명령어 분석:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

docker run --name mycontainer myimage:v1
    │       │        │            │
    │       │        │            └─ 이미지:태그
    │       │        │
    │       │        └─ 컨테이너 이름
    │       │
    │       └─ 이름 지정 옵션
    │
    └─ 실행 명령어

실행 결과:
→ 컨테이너 이름: mycontainer
→ 출력: "Hello, World!"
→ 상태: sleep infinity 대기 중

컨테이너 내부 확인

exec 명령어로 컨테이너 접속:

# 컨테이너 내부 쉘 실행
docker exec -it mycontainer /bin/bash

exec 명령어 옵션:

docker exec 옵션 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

docker exec -it mycontainer /bin/bash
            │ │      │           │
            │ │      │           └─ 실행할 명령어
            │ │      │
            │ │      └─ 컨테이너 이름
            │ │
            │ └─ TTY 할당 (터미널)
            │
            └─ 인터랙티브 모드

-i (--interactive): 표준 입력 유지
-t (--tty): 가상 터미널 할당

컨테이너 내부 확인 작업:

# 1. 루트 디렉토리 확인
ls /

# 출력 예시:
bin  boot  dev  etc  hello.sh  home  lib  ...
                     ↑
                hello.sh가 /에 존재

# 2. 실행 중인 프로세스 확인
ps -Ao pid,cmd

# 출력 예시:
PID CMD
  1 sleep infinity
 12 /bin/bash
 23 ps -Ao pid,cmd

프로세스 분석:

컨테이너 프로세스 구조:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

PID 1: sleep infinity
└─ hello.sh에서 실행한 메인 프로세스
└─ 컨테이너의 생명주기를 유지

PID 12: /bin/bash
└─ docker exec로 실행한 쉘
└─ 임시 프로세스

PID 23: ps -Ao pid,cmd
└─ 현재 실행한 명령어
└─ 일회성 프로세스

특징:
→ systemd, init 같은 데몬 없음
→ 단일 프로세스 중심 실행
→ PID 1이 종료되면 컨테이너 종료
용어 정리

  • docker run: 이미지로부터 컨테이너를 생성하고 실행하는 명령어
  • docker exec: 실행 중인 컨테이너에서 추가 명령어를 실행하는 명령어
  • -it 옵션: -i(인터랙티브 모드)와 -t(TTY 할당)를 조합. 쉘 접속 시 사용
  • PID 1: 컨테이너의 메인 프로세스. 이 프로세스가 종료되면 컨테이너도 종료됨
  • 쓰기 가능 레이어: 컨테이너 생성 시 이미지 위에 추가되는 레이어. 컨테이너의 모든 변경 사항 저장

컨테이너 생명주기 관리

기본 관리 명령어:

# 컨테이너 중지
docker stop mycontainer

# 컨테이너 시작
docker start mycontainer

# 컨테이너 재시작
docker restart mycontainer

# 컨테이너 삭제
docker rm mycontainer

# 실행 중인 컨테이너 강제 삭제
docker rm -f mycontainer

컨테이너 상태 확인:

# 실행 중인 컨테이너만 표시
docker ps

# 모든 컨테이너 표시 (중지된 것 포함)
docker ps -a

# 컨테이너 로그 확인
docker logs mycontainer

# 컨테이너 상세 정보
docker inspect mycontainer

컨테이너 상태 전이:

컨테이너 상태 다이어그램:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[이미지]
    ↓ docker run
[Created]
    ↓ 자동 시작
[Running] ←─────┐
    │           │ docker start
    ↓ docker stop │
[Stopped] ──────┘
    ↓ docker rm
[Deleted]

주요 명령어:
├─ run: 이미지 → 생성 → 실행
├─ stop: 실행 → 중지
├─ start: 중지 → 실행
├─ restart: 재시작
└─ rm: 삭제

백그라운드 실행

detached 모드:

# 백그라운드에서 실행 (-d 옵션)
docker run -d --name mycontainer myimage:v1

# 로그 확인
docker logs mycontainer

# 실시간 로그 스트리밍
docker logs -f mycontainer

포그라운드 vs 백그라운드:

실행 모드 비교:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[포그라운드 모드]
docker run --name mycontainer myimage:v1
    ↓
터미널에 출력 표시
Ctrl+C로 종료 가능
터미널 점유


[백그라운드 모드 (Detached)]
docker run -d --name mycontainer myimage:v1
    ↓
컨테이너 ID만 출력
터미널 즉시 반환
백그라운드 실행 계속

사용 시나리오:
→ 포그라운드: 테스트, 디버깅
→ 백그라운드: 서비스 운영, 데몬
용어 정리

  • -d 옵션 (Detached): 컨테이너를 백그라운드에서 실행하는 옵션. 터미널을 점유하지 않음
  • docker logs: 컨테이너의 표준 출력(stdout)과 표준 에러(stderr)를 확인하는 명령어
  • docker stop/start: 컨테이너를 중지하거나 다시 시작하는 명령어
  • docker rm: 중지된 컨테이너를 삭제하는 명령어. -f 옵션으로 실행 중인 컨테이너도 강제 삭제 가능


2.1.3. Ship: 레지스트리를 사용한 컨테이너 배포

레지스트리 개념

이미지 배포 메커니즘:

레지스트리 기반 배포:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[개발 환경]
    ↓ docker build
[로컬 이미지]
    ↓ docker push
[레지스트리 (Docker Hub)]
    │
    ├─ docker pull → [테스트 환경]
    ├─ docker pull → [스테이징 환경]
    └─ docker pull → [프로덕션 환경]

장점:
✓ 중앙 집중식 관리
✓ 버전 관리
✓ 팀 간 공유
✓ 환경 간 이식성

레지스트리 유형:

레지스트리 종류:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[퍼블릭 레지스트리]
├─ Docker Hub
│  └─ hub.docker.com
│  └─ 무료 퍼블릭 리포지터리
│
├─ GitHub Container Registry (ghcr.io)
│  └─ ghcr.io/{username}/{image}
│
├─ Quay.io
│  └─ Red Hat 제공
│
└─ Google Container Registry (gcr.io)
   └─ GCP 통합

[프라이빗 레지스트리]
├─ Docker Hub Private
├─ AWS ECR (Elastic Container Registry)
├─ Azure Container Registry
├─ Harbor (오픈소스)
└─ 자체 구축 Registry
용어 정리

  • 레지스트리 (Registry): 컨테이너 이미지를 저장하고 배포하는 중앙 저장소
  • Docker Hub: Docker의 공식 퍼블릭 레지스트리. hub.docker.com
  • docker push: 로컬 이미지를 레지스트리에 업로드하는 명령어
  • docker pull: 레지스트리에서 이미지를 다운로드하는 명령어
  • 퍼블릭/프라이빗 레지스트리: 공개 접근 가능 여부에 따른 레지스트리 분류

Docker Hub 사용하기

리포지터리와 태그 구조:

이미지 이름 체계:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[로컬 이미지]
myimage:v1
  │      │
  │      └─ 태그 (버전)
  │
  └─ 리포지터리 이름

[Docker Hub 이미지]
username/myimage:v1
    │       │      │
    │       │      └─ 태그
    │       │
    │       └─ 리포지터리 이름
    │
    └─ 사용자명 (네임스페이스)

[다른 레지스트리]
ghcr.io/username/myimage:v1
  │        │        │      │
  │        │        │      └─ 태그
  │        │        │
  │        │        └─ 리포지터리 이름
  │        │
  │        └─ 사용자명
  │
  └─ 레지스트리 주소

규칙:
→ 레지스트리 생략 시 Docker Hub 기본
→ 사용자명 생략 시 공식 이미지
→ 태그 생략 시 latest 기본
용어 정리

  • 이미지 이름 체계: [레지스트리/]사용자명/리포지터리:태그 형식
  • 네임스페이스: Docker Hub에서 사용자명이나 조직명으로 이미지를 구분하는 단위
  • 공식 이미지 (Official Image): Docker가 검증한 신뢰할 수 있는 이미지. 사용자명 없이 바로 사용 (예: ubuntu, nginx)
  • latest 태그: 태그 미지정 시 기본으로 사용되는 태그. 최신 버전을 의미하지만 명시적 버전 사용 권장

Docker Hub 배포 실습

1. Docker Hub 로그인:

docker login
로그인 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Username: username
Password: ********
    ↓
Login Succeeded
    ↓
자격 증명 저장:
~/.docker/config.json

2. 이미지 태깅:

docker tag myimage:v1 username/myimage:v1
태깅 과정 설명:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[원본 이미지]
myimage:v1
    ↓ docker tag
[새 태그 생성]
username/myimage:v1

특징:
→ 동일한 이미지에 다른 이름 부여
→ 실제 복사 없음 (참조만 생성)
→ 저장 공간 추가 사용 안 함

3. 이미지 확인:

docker image ls username/myimage:v1
출력 예시:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

REPOSITORY         TAG    IMAGE ID      CREATED       SIZE
username/myimage   v1     abc123def     5 mins ago    117MB

확인:
→ myimage:v1과 IMAGE ID 동일
→ 같은 이미지를 가리키는 다른 이름

4. 이미지 푸시 (업로드):

docker push username/myimage:v1
푸시 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[로컬 이미지]
username/myimage:v1
    ↓
[레이어별 업로드]
├─ Layer 1: ubuntu:22.04 (스킵 - 이미 존재)
├─ Layer 2: hello.sh 복사 (업로드)
├─ Layer 3: chmod 실행 (업로드)
└─ Layer 4: ENTRYPOINT (업로드)
    ↓
[Docker Hub]
https://hub.docker.com/r/username/myimage

최적화:
→ 공통 레이어는 재사용
→ 새 레이어만 업로드
→ 네트워크 효율성 증대

5. 이미지 풀 (다운로드):

# 다른 환경에서 이미지 다운로드
docker pull username/myimage:v1
풀 과정:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Docker Hub]
username/myimage:v1
    ↓
[레이어별 다운로드]
├─ Layer 1: ubuntu:22.04 (다운로드)
├─ Layer 2: hello.sh (다운로드)
├─ Layer 3: chmod (다운로드)
└─ Layer 4: ENTRYPOINT (다운로드)
    ↓
[로컬 이미지]
username/myimage:v1

확인:
→ 모든 레이어 다운로드 완료
→ 어디서든 동일한 환경 구성

6. 다운로드한 이미지 확인:

docker images

7. 새 컨테이너 실행:

docker run --name mycontainer2 username/myimage:v1

배포 시나리오

실무 배포 흐름:

CI/CD 파이프라인 예시:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[개발 환경]
    ↓
코드 커밋 → Git
    ↓
[CI 서버]
    ↓
1. 코드 체크아웃
2. docker build -t myapp:${VERSION}
3. 테스트 실행
4. docker tag myapp:${VERSION} registry.com/myapp:${VERSION}
5. docker push registry.com/myapp:${VERSION}
    ↓
[레지스트리]
registry.com/myapp:1.0.0
registry.com/myapp:1.0.1
registry.com/myapp:latest
    ↓
[배포]
    ├─→ 테스트 환경
    │   docker pull registry.com/myapp:1.0.1
    │   docker run myapp:1.0.1
    │
    ├─→ 스테이징 환경
    │   검증 후 승인
    │
    └─→ 프로덕션 환경
        Kubernetes 배포
        또는 docker-compose

태깅 전략:
├─ ${VERSION}: 특정 버전 (1.0.0, 1.0.1)
├─ latest: 최신 안정 버전
├─ develop: 개발 브랜치
└─ ${GIT_SHA}: 커밋 해시

핵심 요약

도커 작업 흐름 정리:

Build, Ship, Run 요약:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[Build - 이미지 작성]
├─ Dockerfile 작성
│  └─ 컨테이너 구성 명령 정의
│
├─ docker build -t 이미지명:태그 경로
│  └─ 레이어별 이미지 생성
│
└─ 결과: 재사용 가능한 이미지
   └─ 읽기 전용 템플릿

[Ship - 이미지 배포]
├─ docker tag 로컬이미지 레지스트리/이미지
│  └─ 배포용 태그 부여
│
├─ docker push 레지스트리/이미지
│  └─ 레지스트리에 업로드
│
├─ docker pull 레지스트리/이미지
│  └─ 레지스트리에서 다운로드
│
└─ 결과: 환경 간 이식성 확보
   └─ 동일한 이미지 공유

[Run - 컨테이너 실행]
├─ docker run 이미지명
│  └─ 컨테이너 생성 및 실행
│
├─ docker exec -it 컨테이너 명령어
│  └─ 실행 중인 컨테이너 접속
│
├─ docker logs 컨테이너
│  └─ 로그 확인
│
└─ 결과: 격리된 실행 환경
   └─ 독립적인 프로세스 공간

핵심 개념:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

[이미지]
→ 읽기 전용 템플릿
→ 레이어 구조
→ 재사용 가능

[컨테이너]
→ 이미지의 실행 인스턴스
→ 쓰기 가능 레이어 추가
→ 독립된 프로세스 공간

[레지스트리]
→ 이미지 저장소
→ 버전 관리
→ 팀 간 공유

결론:
→ Build: 한 번 작성
→ Ship: 어디든 배포
→ Run: 동일하게 실행
→ 컨테이너의 이식성 극대화

참고 자료

공식 문서:

베스트 프랙티스: